/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.business.customer.core.domain.service;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.business.customer.core.domain.Customer;
import info.peigen.hotpot.business.customer.core.domain.CustomerLog;
import info.peigen.hotpot.business.customer.core.domain.action.CustomerCheck;
import info.peigen.hotpot.business.customer.core.domain.action.CustomerConverter;
import info.peigen.hotpot.business.customer.core.domain.action.CustomerDomainAction;
import info.peigen.hotpot.business.customer.core.domain.operator.CustomerOperator;
import info.peigen.hotpot.business.customer.core.domain.operator.CustomerOperatorActive;
import info.peigen.hotpot.business.customer.core.repository.entity.CustomerAuthenticationBaseEntity;
import info.peigen.hotpot.business.customer.core.repository.service.CustomerLogRepository;
import info.peigen.hotpot.business.customer.core.util.SafeUtil;
import info.peigen.hotpot.business.customer.core.util.VerifyUtil;
import info.peigen.hotpot.business.customer.facade.enums.CustomerAction;
import info.peigen.hotpot.business.customer.facade.enums.CustomerField;
import info.peigen.hotpot.business.customer.facade.enums.CustomerLogType;
import info.peigen.hotpot.business.customer.facade.info.CustomerInfo;
import info.peigen.hotpot.business.customer.facade.order.authentication.BopsCustomerUpdateControlPeopleOrder;
import info.peigen.hotpot.business.customer.facade.order.authentication.CustomerApplyAuthenticationOrder;
import info.peigen.hotpot.business.customer.facade.order.authentication.CustomerApplyPersonalAuthenticationOrder;
import info.peigen.hotpot.business.customer.facade.order.authentication.CustomerAuditAuthenticationOrder;
import info.peigen.hotpot.business.customer.facade.order.check.CustomerDuplicateCheckOrder;
import info.peigen.hotpot.business.customer.facade.order.check.CustomerPayPasswordCheckOrder;
import info.peigen.hotpot.business.customer.facade.order.log.CustomerLogOrder;
import info.peigen.hotpot.business.customer.facade.order.login.CustomerLoginOrder;
import info.peigen.hotpot.business.customer.facade.order.query.CustomerQueryOrder;
import info.peigen.hotpot.business.customer.facade.order.register.CustomerForgotPasswordOrder;
import info.peigen.hotpot.business.customer.facade.order.update.CustomerUpdateOrder;
import info.peigen.hotpot.business.customer.facade.result.check.CustomerDuplicateCheckResult;
import info.peigen.hotpot.business.customer.facade.result.login.CustomerLoginResult;
import info.peigen.hotpot.business.customer.facade.result.query.CustomerQueryResult;
import info.peigen.hotpot.business.customer.facade.result.register.CustomerRegisterResult;
import info.peigen.hotpot.business.customer.facade.result.register.CustomerUpdateResult;
import info.peigen.hotpot.common.core.util.PasswordUtils;
import info.peigen.hotpot.common.service.base.DomainServiceInvoke;
import info.peigen.hotpot.common.service.base.ServiceContext;
import lombok.extern.slf4j.Slf4j;
import org.beetl.sql.core.query.LambdaQuery;
import org.beetl.sql.core.query.Query;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.TranAnno;
import org.noear.solon.data.tran.TranPolicy;
import org.noear.solon.data.tran.TranUtils;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static org.noear.solon.data.tran.TranIsolation.read_committed;

/**
 * <b>(CustomerDomainService)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2021/7/29
 */
@Slf4j
public class CustomerDomainService extends AbstractCustomerDomainService {
    /**
     * 默认不能与过去密码相同的数量
     */
    private static final int                   DEFAULT_OLD_PASSWORD_NUM = 5;
    @Inject
    private              CustomerLogRepository customerLogRepository;

    public void register(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {

            @Override
            public void invoke(ServiceContext serviceContext) {
                Customer customer = (Customer) new Customer().create(serviceContext.getOrder());
                CustomerCheck.registerCheck(customer);
                serviceContext.setDomain(customer);

                CustomerRegisterResult customerRegisterResult = (CustomerRegisterResult) serviceContext.getResult();
                CustomerLogOrder       customerLogOrder       = CustomerLogOrder.builder().customerId(customer.getCustomerId()).type(CustomerLogType.EditLoginPassword).password(customer.getMasterOperator().getLoginPassword()).build();
                customerRepository.updateCustomerLog(customerLogOrder);
                customerRegisterResult.setInfo(CustomerConverter.convertCustomerInfo(customer));
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.store((Customer) serviceContext.getDomain());
            }

            @Override
            public void handleException(Exception e, ServiceContext serviceContext) {
                DomainServiceInvoke.super.handleException(e, serviceContext);
                if (e instanceof SQLIntegrityConstraintViolationException) {
                    serviceContext.getResult().setFail("客户已注册");
                }
            }
        });
    }

    public void checkPayPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerPayPasswordCheckOrder order            = ((CustomerPayPasswordCheckOrder) serviceContext.getOrder());
                Customer                      customer         = customerRepository.active(order.getCustomerId());
                CustomerOperator              customerOperator = CustomerDomainAction.getCustomerOperator(customer, order.getOperatorId());
                String                        curPayPassword   = order.getPayPassword();
                String                        rawPayPassword   = customerOperator.getPayPassword();
                String                        salt             = customerOperator.getSalt();

                serviceContext.setDomain(customer);

                Assert.notNull(rawPayPassword, "您未设置交易密码");

                CustomerOperatorActive activeInfo            = customerOperator.getActiveInfo();
                Integer                payPasswordErrorTimes = activeInfo.getPayPasswordErrorTimes();
                int                    errorTimes            = (payPasswordErrorTimes == null ? 0 : payPasswordErrorTimes);
                Date                   lastFailTime          = activeInfo.getPayLastFailTime();
                long                   interval              = (null != lastFailTime ? DateUtil.between(lastFailTime, new Date(), DateUnit.SECOND) : 0);

                if (interval >= SafeUtil.DEFAULT_MAX_LOCK_TIME) {
                    //  距离最近一次密码错误的时间间隔>=最大账号锁定时间
                    //  密码已错误次数设置为0，如果后续密码验证还是错了，则可从0开始计数
                    errorTimes = 0;
                }

                // 错误次数达到最大错误次数
                if (errorTimes >= SafeUtil.DEFAULT_FAIL_TIMES) {
                    long   lockTime = SafeUtil.getLockTime(errorTimes);
                    long   between  = lockTime - interval;
                    String format   = String.format("交易密码错误 %s 次，请 %s后再试", errorTimes, SafeUtil.format(between));
                    // 距离最近一次密码错误的时间间隔<=最大账号锁定时间
                    if (between >= 0) {
                        serviceContext.getResult().setParameter("error_times", errorTimes);
                        serviceContext.getResult().setParameter("residue_seconds", between);
                        serviceContext.getResult().setFail(format);
                        return;
                    }
                }

                if (!CustomerDomainAction.passwordEquals(curPayPassword, rawPayPassword, salt)) {
                    activeInfo.setPayLastFailTime(new Date());
                    // 密码验证错误，在密码已错误次数基础上加1
                    errorTimes += 1;
                    activeInfo.setPayPasswordErrorTimes(errorTimes);
                    customerOperator.setUpdateTime(DateUtil.date());

                    int    residueTimes = SafeUtil.DEFAULT_FAIL_TIMES - errorTimes;
                    String format       = null;
                    long   lockTime     = SafeUtil.getLockTime(errorTimes);
                    if (residueTimes > 0) {
                        format = String.format("交易密码错误, 您还有 %s 次机会", residueTimes);
                    } else {
                        format = String.format("交易密码错误 %s 次，请 %s后再试", errorTimes, SafeUtil.format(lockTime));
                        serviceContext.getResult().setParameter("residue_seconds", lockTime);
                    }

                    serviceContext.getResult().setParameter("error_times", activeInfo.getPayPasswordErrorTimes());
                    serviceContext.getResult().setFail(format);
                    return;
                }

                customerOperator.getActiveInfo().setPayPasswordErrorTimes(0);
                customerOperator.setUpdateTime(DateUtil.date());

                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators(((Customer) serviceContext.getDomain()));
            }
        });
    }

    public void updateVerify(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                CustomerOperator customerOperator = CustomerDomainAction.getCustomerOperator(customer, order.getOperatorId());
                String           loginName        = customerOperator.getLoginName();
                String           loginPassword    = customerOperator.getLoginPassword();


                CustomerUpdateResult result = (CustomerUpdateResult) serviceContext.getResult();

                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }
        });
    }

    public void updateEmail(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                checkUpdateToken(customer, order);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                Assert.isNull(customerRepository.getCustomerOperatorByMobileOrEmail(order.getEmail()), "该邮箱已存在, 不能作为新的邮箱");

                CustomerDomainAction.updateEmail(customer, order);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateBaseInfo((Customer) serviceContext.getDomain());
            }
        });
    }

    public void updateMobile(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                Assert.isNull(customerRepository.activeByLoginName(order.getMobile()), "该手机号已被使用，请使用其他手机号");
                checkUpdateToken(customer, order);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                Assert.isNull(customerRepository.getCustomerOperatorByMobileOrEmail(order.getMobile()), "该手机号已存在, 不能作为新的手机号码");

                CustomerDomainAction.updateMobile(customer, order);

                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateBaseInfo((Customer) serviceContext.getDomain());
            }
        });
    }

    /**
     * 对操作token进行检查
     * 1.传进来的操作token必须有效，未过期，且是用于操作的token（因为操作token生成的方法和登录token一样，这样做是为了避免登录token被作为操作token传入）
     * 2.order中的OperatorId与缓存的order中的OperatorId相等，保证是同一个Operator在操作
     * 3.order中的CustomerAction与缓存的order中的CustomerAction相等，保证一个操作token只能进行相应的动作（{@link CustomerDomainAction}）
     */
    private void checkUpdateToken(Customer customer, CustomerUpdateOrder order) {
        String           key              = VerifyUtil.getTokenCacheKey(order.getToken());
        CustomerOperator customerOperator = CustomerDomainAction.getCustomerOperator(customer, order.getOperatorId());
        String           loginName        = customerOperator.getLoginName();
        String           loginPassword    = customerOperator.getLoginPassword();

        Assert.isTrue(VerifyUtil.verify(order.getToken(), loginName, loginPassword), "无效token");

        CustomerUpdateOrder cacheOrder = null;

        Assert.isTrue(StrUtil.equals(order.getOperatorId(), cacheOrder.getOperatorId()), "您不是该token持有者");

        CustomerAction a1 = CustomerAction.getByCode(order.getProductCode());
        CustomerAction a2 = CustomerAction.getByCode(cacheOrder.getProductCode());
        Assert.isTrue(Objects.equals(a1, a2), "您的token无法进行该操作");
    }

    public void updatePayPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                CustomerDomainAction.updatePayPassword(customer, order);
                CustomerLogOrder customerLogOrder = checkOldPassword(customer, order.getOperatorId(), order.getNewLoginPassword(), CustomerLogType.EditPayPassword);

                serviceContext.setObject(customerLogOrder);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateCustomerLog((CustomerLogOrder) serviceContext.getObject());
            }
        });
    }

    public void updateLoginPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                CustomerDomainAction.updateLoginPassword(customer, order);
                CustomerLogOrder customerLogOrder = checkOldPassword(customer, order.getOperatorId(), order.getNewLoginPassword(), CustomerLogType.EditLoginPassword);

                serviceContext.setObject(customerLogOrder);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateCustomerLog((CustomerLogOrder) serviceContext.getObject());
            }
        });
    }

    public void updateEnglishName(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                Assert.notNull(customer, "客户信息不存在");
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateAuthenticationBaseInfo((Customer) serviceContext.getDomain());
            }
        });
    }

    public void forgotPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerForgotPasswordOrder order    = (CustomerForgotPasswordOrder) serviceContext.getOrder();
                Customer                    customer = customerRepository.activeByLoginName(order.getMobile());
                CustomerCheck.loginCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerForgotPasswordOrder order         = (CustomerForgotPasswordOrder) serviceContext.getOrder();
                Customer                    customer      = (Customer) serviceContext.getDomain();
                List<CustomerLog>           logs          = customerRepository.activeCustomerLog(customer.getCustomerId(), CustomerLogType.EditLoginPassword, null);
                List<CustomerLog>           forthHeadList = new ArrayList<>();
                if (logs.size() > 4) {
                    forthHeadList = logs.subList(0, 4);
                } else {
                    forthHeadList = logs.subList(0, logs.size());
                }
                CustomerDomainAction.setUpPassword(customer, order, forthHeadList);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                // 修改密码向表里插条数据
                CustomerLogOrder customerLogOrder = CustomerLogOrder.builder().customerId(((Customer) serviceContext.getDomain()).getCustomerId()).type(CustomerLogType.EditLoginPassword).password(((Customer) serviceContext.getDomain()).getMasterOperator().getLoginPassword()).build();
                customerRepository.updateCustomerLog(customerLogOrder);
                customerRepository.deleteCustomerLog(((Customer) serviceContext.getDomain()).getCustomerId(), CustomerLogType.ForgetLoginPassword);

            }
        });
    }

    public Customer login(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerLoginOrder order    = (CustomerLoginOrder) serviceContext.getOrder();
                Customer           customer = customerRepository.activeByLoginName(order.getLoginName());
                CustomerCheck.loginCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerLoginOrder order    = (CustomerLoginOrder) serviceContext.getOrder();
                Customer           customer = (Customer) serviceContext.getDomain();
                Date               date     = DateUtil.beginOfDay(DateUtil.date());
                List<CustomerLog>  logs     = customerRepository.activeCustomerLog(customer.getCustomerId(), CustomerLogType.ForgetLoginPassword, date);
                Assert.isTrue(logs.size() < 5, "登录密码输入错误超过5次，请24小时后重试!");

                try {
                    CustomerDomainAction.login(customer, order);
                } catch (Exception e) {
                    CustomerLogOrder customerLogOrder = CustomerLogOrder.builder().customerId(customer.getCustomerId()).type(CustomerLogType.ForgetLoginPassword).build();
                    customerRepository.updateCustomerLog(customerLogOrder);
                    serviceContext.getResult().setFail(e.getMessage());
                    return;
                }


                CustomerLoginResult customerLoginResult = (CustomerLoginResult) serviceContext.getResult();
                customerLoginResult.setInfo(CustomerConverter.convertCustomerInfo(customer));

                // 填充登录信息
                customerRepository.login(customer);

                // 生成token && 设置token缓存有效时间
                // String token = JwtUtil.sign(order.getLoginName(), order.getLoginPassword());
                // redisUtil.set(JwtUtil.getTokenCacheKey(token), token, JwtUtil.EXPIRE_TIME / 1000);

                // customerOperatorLoginResult.setToken(token);

                // 登录成功后插入登录成功日志
                CustomerLogOrder customerLogOrder = CustomerLogOrder.builder().customerId(customer.getCustomerId()).type(CustomerLogType.LoginSuccess).build();
                customerRepository.updateCustomerLog(customerLogOrder);

                // 登录成功后删除登录密码错误日志
                customerRepository.deleteCustomerLog(customer.getCustomerId(), CustomerLogType.ForgetLoginPassword);
                serviceContext.getResult().setSuccessNotFee();
            }

        });
        return (Customer) serviceContext.getDomain();
    }

    public Customer queryCustomerInfo(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerQueryOrder order    = (CustomerQueryOrder) serviceContext.getOrder();
                Customer           customer = customerRepository.active(order.getCustomerId());
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerInfo customerInfo = CustomerConverter.convertCustomerInfo((Customer) serviceContext.getDomain());
                ((CustomerQueryResult) serviceContext.getResult()).fromSuccess(customerInfo);
            }

        });
        return (Customer) serviceContext.getDomain();
    }

    public void duplicateCheck(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerDuplicateCheckOrder customerDuplicateCheckOrder = (CustomerDuplicateCheckOrder) serviceContext.getOrder();

                LambdaQuery<CustomerAuthenticationBaseEntity> lambdaQuery = customerAuthenticationBaseRepository.getMapper().createLambdaQuery();
                lambdaQuery.andNotEq(CustomerAuthenticationBaseEntity::getCustomerId, Query.filterEmpty(customerDuplicateCheckOrder.getCustomerId()));

                if (customerDuplicateCheckOrder.getCustomerField().isInList(CustomerField.enterpriseName)) {
                    lambdaQuery.andEq(CustomerAuthenticationBaseEntity::getEnterpriseName, Query.filterEmpty(customerDuplicateCheckOrder.getValue()));
                }
                if (customerDuplicateCheckOrder.getCustomerField().isInList(CustomerField.uniformSocialCreditCode)) {
                    lambdaQuery.andEq(CustomerAuthenticationBaseEntity::getUniformSocialCreditCode, Query.filterEmpty(customerDuplicateCheckOrder.getValue()));
                }

                List<CustomerAuthenticationBaseEntity> customerAuthenticationBaseEntityList = lambdaQuery.select();
                CustomerDuplicateCheckResult           customerDuplicateCheckResult         = (CustomerDuplicateCheckResult) serviceContext.getResult();
                if (!CollUtil.isEmpty(customerAuthenticationBaseEntityList)) {
                    customerDuplicateCheckResult.fromSuccess(false);
                } else {
                    customerDuplicateCheckResult.fromSuccess(true);
                }
            }

        });
    }

    public void applyAuthentication(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerApplyAuthenticationOrder order    = (CustomerApplyAuthenticationOrder) serviceContext.getOrder();
                Customer                         customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.applyAuthenticationCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerApplyAuthenticationOrder order    = (CustomerApplyAuthenticationOrder) serviceContext.getOrder();
                Customer                         customer = (Customer) serviceContext.getDomain();
                CustomerDomainAction.applyAuthentication(customer, order);

                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                Customer customer = (Customer) serviceContext.getDomain();
                customerRepository.applyAuthentication(customer);
            }
        });
    }

    public void applyPersonalAuthentication(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerApplyPersonalAuthenticationOrder order    = (CustomerApplyPersonalAuthenticationOrder) serviceContext.getOrder();
                Customer                                 customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.applyAuthenticationCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerApplyPersonalAuthenticationOrder order    = (CustomerApplyPersonalAuthenticationOrder) serviceContext.getOrder();
                Customer                                 customer = (Customer) serviceContext.getDomain();
                CustomerDomainAction.applyPersonalAuthentication(customer, order);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                Customer customer = (Customer) serviceContext.getDomain();
                customerRepository.applyAuthentication(customer);
            }
        });
    }

    public void auditAuthentication(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerAuditAuthenticationOrder order    = (CustomerAuditAuthenticationOrder) serviceContext.getOrder();
                Customer                         customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.auditAuthenticationCheck(customer);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerAuditAuthenticationOrder order    = (CustomerAuditAuthenticationOrder) serviceContext.getOrder();
                Customer                         customer = (Customer) serviceContext.getDomain();
                CustomerDomainAction.auditAuthentication(customer, order);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                try {
                    TranUtils.execute(new TranAnno().isolation(read_committed).policy(TranPolicy.required),
                            () -> {
                                // 实名审核
                                Customer customer = (Customer) serviceContext.getDomain();
                                customerRepository.auditAuthentication(customer);
                            });
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    public void updateControlPeople(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                BopsCustomerUpdateControlPeopleOrder order    = (BopsCustomerUpdateControlPeopleOrder) serviceContext.getOrder();
                Customer                             customer = customerRepository.active(order.getCustomerId());
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                BopsCustomerUpdateControlPeopleOrder order    = (BopsCustomerUpdateControlPeopleOrder) serviceContext.getOrder();
                Customer                             customer = (Customer) serviceContext.getDomain();
                CustomerDomainAction.updateControlPeople(customer, order);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                Customer customer = (Customer) serviceContext.getDomain();
                customerRepository.updateControlPeople(customer);
            }
        });
    }

    public void forgotPayPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.loginCheck(customer);
                checkUpdateToken(customer, order);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();
                CustomerDomainAction.updatePayPassword(customer, order);
                CustomerLogOrder customerLogOrder = checkOldPassword(customer, order.getOperatorId(), order.getNewLoginPassword(), CustomerLogType.EditPayPassword);

                serviceContext.setObject(customerLogOrder);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateCustomerLog((CustomerLogOrder) serviceContext.getObject());
            }
        });
    }

    public void forgotLoginPassword(ServiceContext serviceContext) {
        execute(serviceContext, new DomainServiceInvoke() {
            @Override
            public void activeDomain(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = customerRepository.active(order.getCustomerId());
                CustomerCheck.forgetPasswordCheck(customer);
                checkUpdateToken(customer, order);
                serviceContext.setDomain(customer);
            }

            @Override
            public void invoke(ServiceContext serviceContext) {
                CustomerUpdateOrder order    = (CustomerUpdateOrder) serviceContext.getOrder();
                Customer            customer = (Customer) serviceContext.getDomain();

                CustomerDomainAction.updateLoginPassword(customer, order);
                CustomerLogOrder customerLogOrder = checkOldPassword(customer, order.getOperatorId(), order.getNewLoginPassword(), CustomerLogType.EditLoginPassword);

                serviceContext.setObject(customerLogOrder);
                serviceContext.setDomain(customer);
                serviceContext.getResult().setSuccessNotFee();
            }

            @Override
            public void handleStoreDataEvent(ServiceContext serviceContext) {
                customerRepository.updateOperators((Customer) serviceContext.getDomain());
                customerRepository.updateCustomerLog((CustomerLogOrder) serviceContext.getObject());
            }
        });
    }

    public CustomerLogOrder checkOldPassword(Customer customer, String operatorId, String newPassword, CustomerLogType customerLogType) {
        List<CustomerLog> customerLogs = customerRepository.activeCustomerLogByOperatorId(operatorId, customerLogType, null, DEFAULT_OLD_PASSWORD_NUM);

        CustomerOperator customerOperator = CustomerDomainAction.getCustomerOperator(customer, operatorId);

        customerLogs.forEach(customerLog -> {
            boolean isEquals = CustomerDomainAction.passwordEquals(newPassword, customerLog.getPassword(), customerOperator.getSalt());
            Assert.isFalse(isEquals, "新密码不能与已使用过的密码相同");
        });

        String encryptedPw = PasswordUtils.generatePwdEncrypt(newPassword, customerOperator.getSalt());

        return CustomerLogOrder.builder().customerId(customer.getCustomerId()).operatorId(operatorId).password(encryptedPw).type(customerLogType).build();
    }
}